package com.heima.search.service.impl;

import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.search.dto.UserSearchDto;
import com.heima.model.search.pojo.ApAssociateWords;
import com.heima.search.model.Trie;
import com.heima.search.service.ApAssociateWordsService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.lang.ref.SoftReference;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @作者 itcast
 * @创建日期 2021/6/8 15:33
 *
 * xml bean     init-method
 **/
@Service  //, InitializingBean
public class ApAssociateWordsServiceImpl implements ApAssociateWordsService,InitializingBean{//InitiallizingBean   容器初始化  时执行方法,IOC容器的生命周期    IOC容器管理一个对象    对象一创建执行方法
    @Autowired
    MongoTemplate mongoTemplate;

    //模糊查询效率低
    @Override
    public ResponseResult findAssociate(UserSearchDto userSearchDto) {
       if (StringUtils.isBlank(userSearchDto.getSearchWords())){
           //关键为空
           //打印日志
           return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);

       }
       //利用mongo查询模糊查询敏感词库
        List<ApAssociateWords> associateWords = mongoTemplate.find(Query.query(Criteria.where("associateWords").regex(".*" + userSearchDto.getSearchWords() + ".*")), ApAssociateWords.class);
       //regex  通过正则表达式   模糊查询   关键词
       return ResponseResult.okResult(associateWords);

    }





    // 变量 引用4中类型:   强引用   数据会一直存在          软引用    jvm内存不足时会被垃圾回收                 弱引用  虚引用

    //定义全局变量    局部变量  在线程执行完后会被垃圾回收
    public static SoftReference<Trie> softReference;//内存被占用的问题   软引用   指定泛型  是Trie

    //利用trie树查询     (字典树)  优化关键联想词查询    Trie树   前缀查询  里面是map结构   类似 二分树   DFA算法
    @Override
    public ResponseResult findAssociateV2(UserSearchDto userSearchDto) {
        // 当项目启动时  初始化Trie树
        Trie trie = softReference.get();   //树缓存到内存中   需要准备共有的变量     局部变量会被垃圾回收(方法一结束,对象就被回收)
        if(trie == null){
            initTrieData();
        }
        // 查询联想词  从Trie树种获取包含的联想词
        List<String> dataList = trie.getData(userSearchDto.getSearchWords());//dto中传入关键词     根据前缀 获取所有联想词汇
        List<ApAssociateWords> associateWords = dataList.stream().map(str -> {
            ApAssociateWords words = new ApAssociateWords();//返回给前端
            words.setAssociateWords(str);
            return words;
        }).collect(Collectors.toList());//收集成集合
        // 返回结果
        return ResponseResult.okResult(associateWords);
    }

    public void initTrieData(){
        List<ApAssociateWords> list = mongoTemplate.findAll(ApAssociateWords.class);
        Trie trie = new Trie();//新建一个trie  树
        // 将所有联想词 存入到Trie树种
        list.stream().map(ApAssociateWords::getAssociateWords).forEach(trie::insert);//得到每一个关键词     通过方法引用  保存到  trie中
        softReference = new SoftReference<Trie>(trie);//创建一个软引用
    }

    @Override
    public void afterPropertiesSet() throws Exception {
          // 当前Spring实例准备完毕后  执行的初始化方法   InitializingBean
        System.out.println("=============初始化方法=============");
        initTrieData();//数据保存到trie树中
    }
}
